package com.example.filter;

import com.example.entity.RestBean;
import com.example.utils.Const;
import com.example.utils.FlowUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.PrintWriter;

import static com.example.utils.Const.*;

//限流过滤器

@Component
@Order(ORDER_FLOW_LIMIT)
public class FlowLimitFilter extends HttpFilter {


    //redis
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //限流工具类
    @Resource
    private FlowUtils flowUtils;

    //指定时间内最大请求次数限制
    @Value("${spring.web.flow.limit}")
    int limit;
    //计数时间周期
    @Value("${spring.web.flow.period}")
    int period;
    //超出请求限制封禁时间
    @Value("${spring.web.flow.block}")
    int block;

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String address = request.getRemoteAddr();
        if(this.tryCount(address)){
            //继续执行
            chain.doFilter(request, response);
        }else {
            this.writeBlockMessage(response);
        }

    }

    /**
     * 尝试对用户请求进行计数(使用redis)，超过一定数量视为非法操作
     * @param address 用户ip
     * @return 是否超出限制
     */
    private boolean tryCount(String address){
        synchronized (address.intern()){
            if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(FLOW_LIMIT_BLACK_LIST + address))){
                //说明用户已经在黑名单中
                return false;
            }else {
                String counterKey = FLOW_LIMIT_COUNTER + address;
                String blockKey = FLOW_LIMIT_BLACK_LIST + address;
                //TODO:不懂
                return flowUtils.limitPeriodCheck(counterKey, blockKey, block, limit, period);
            }
        }
    }

    /**
     * 限流返回的错误信息
     * @param response response
     * @throws IOException io
     */
    private void writeBlockMessage(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(RestBean.forbidden("操作频繁，请稍后再试！").asJsonString());
    }

}
